package com.fsoft.core.utils.excel;

import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

import org.codehaus.groovy.control.CompilerConfiguration;
import org.springframework.stereotype.Service;

import com.fsoft.core.utils.FileManager;
import com.fsoft.core.utils.OgnlUtils;
import com.fsoft.core.utils.RetVo;
import com.greenpineyu.fel.FelEngine;
import com.greenpineyu.fel.FelEngineImpl;
import com.greenpineyu.fel.context.FelContext;

import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import groovy.lang.Script;

/***
 * 脚本引擎管理 表达式引擎 FelEngine,groovy,javascript
 * @package com.fsoft.core.utils.excel
 * @author fish
 * @email it.fish2010@foxmail.com
 * @date 2021-09-30
 * @desc
 *
 */
@Service
public class ScriptEngineServiceImpl {
	private final String UTF8 = "UTF-8";
	/**
	 * 表达式引擎
	 */
	private FelEngine felEngine = null;

	public Object evalJavaScript(String script, Map<String, Object> params) throws ScriptException {
		String md5Code = OgnlUtils.md5(script);
		CompiledScript compiled = null;
		ScriptEngine javaScriptEngine = null;
		if (scriptCache.containsKey(md5Code)) {
			compiled = (CompiledScript) scriptCache.get(md5Code);
			javaScriptEngine = compiled.getEngine();
		} else {
			javaScriptEngine = (new ScriptEngineManager()).getEngineByName("JavaScript");
			Compilable compilable = (Compilable) javaScriptEngine;
			compiled = compilable.compile(script);
		}
		if (null != params && params.size() > 0) {
			for (String str : params.keySet()) {
				javaScriptEngine.put(str, params.get(str));
			}
		}
		return compiled.eval();
	}

	public Object evalJavaScript(String script) throws ScriptException {
		return evalJavaScript(script, null);
	}

	/**
	 * 执行表达式返回结果
	 * 
	 * @author gk @date 2017-4-23 下午4:01:33
	 * @param script
	 * @return
	 */
	public Object evalFelScript(String script) {
		felEngine = new FelEngineImpl();
		return felEngine.eval(script);
	}

	/**
	 * 根据表达式的context执行表达式
	 * 
	 * @author gk @date 2017-4-23 下午4:01:48
	 * @param script
	 * @param cxt
	 * @return
	 */
	public Object evalFelScript(String script, FelContext cxt) {
		return felEngine.eval(script, cxt);
	}

	public FelContext getFelContext() {
		return felEngine.getContext();
	}

	private static Map<String, Object> scriptCache = new ConcurrentHashMap<String, Object>();

	/**
	 * 执行groovy脚本
	 * 
	 * @author gk @date 2018年11月26日 下午5:24:27
	 * @param script    脚本字符串
	 * @param variables 传递的参数Map
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public RetVo evalGroovyScript(String script, Map<String, Object> variables) throws Exception {
		String md5Code = OgnlUtils.md5(script);
		Script shell = null;
		Binding binding = new Binding();

		if (variables != null && variables.size() > 0) {
			binding.getVariables().putAll(variables);
		}
		// 缓存中读取,避免重复创建
		if (scriptCache.containsKey(md5Code)) {
			shell = (Script) scriptCache.get(md5Code);
		} else {
			CompilerConfiguration config = new CompilerConfiguration();
			config.setSourceEncoding(UTF8);
			GroovyShell groovyShell = new GroovyShell(config);
			shell = groovyShell.parse(script);
			scriptCache.put(md5Code, shell);
		}
		shell.setBinding(binding);
		Object ret = shell.run();
		return RetVo.ok(ret);
	}

	/**
	 * 执行groovy脚本文件
	 * 
	 * @author gk @date 2018年11月26日 下午5:25:00
	 * @param file      文件
	 * @param variables 传递的变量Map
	 * @return
	 * @throws Exception
	 */
	public RetVo evalGroovyScript(File file, Map<String, Object> variables) throws Exception {
		String script = FileManager.readFileContent(file);
		return evalGroovyScript(script, variables);
	}

	/**
	 * 执行groovy脚本流 注意：编码UTF-8
	 * 
	 * @author gk @date 2018年11月26日 下午5:25:00
	 * @param File      文件
	 * @param variables 传递的变量Map
	 * @return
	 * @throws Exception
	 */
	public RetVo evalGroovyScript(InputStream is, Map<String, Object> variables) throws Exception {
		String script = FileManager.readFileContent(is);
		return evalGroovyScript(script, variables);
	}

	/**
	 * 执行groovy脚本URL路径
	 * 
	 * @author gk @date 2018年11月26日 下午5:26:23
	 * @param url       路径
	 * @param variables 传递的变量Map
	 * @return
	 * @throws Exception
	 */
	public RetVo evalGroovyScript(URL url, Map<String, Object> variables) throws Exception {
		return evalGroovyScript(url.openStream(), variables);
	}

	/***
	 * 执行groovy脚本里面的方法
	 * @author fsoft
	 * @date 2021-09-30
	 * @param file
	 * @param method
	 * @param obj
	 * @return
	 * @throws Exception
	 */
	public RetVo evalGroovyScriptMethod(File file, String method, Object obj) throws Exception {
		String script = FileManager.readFileContent(file);
		String md5Code = OgnlUtils.md5(script);
		Script shell = null;
		// 缓存中读取,避免重复创建
		if (scriptCache.containsKey(md5Code)) {
			shell = (Script) scriptCache.get(md5Code);
		} else {
			CompilerConfiguration config = new CompilerConfiguration();
			config.setSourceEncoding(UTF8);
			GroovyShell groovyShell = new GroovyShell(config);
			shell = groovyShell.parse(script);
			scriptCache.put(md5Code, shell);
		}
		Object ret = shell.invokeMethod(method, obj);
		return RetVo.ok(ret);
	}

	public RetVo checkGroovyScript(String script) {
		String md5Code = OgnlUtils.md5(script);
		Script shell = null;
		// 缓存中读取,避免重复创建
		if (scriptCache.containsKey(md5Code)) {
			shell = (Script) scriptCache.get(md5Code);
		} else {
			try {
				CompilerConfiguration config = new CompilerConfiguration();
				config.setSourceEncoding(UTF8);
				GroovyShell groovyShell = new GroovyShell();
				shell = groovyShell.parse(script);
				scriptCache.put(md5Code, shell);
			} catch (Exception e) {
				return RetVo.error(e.getMessage());
			}
		}
		return RetVo.ok();
	}
}
